package org.black.phoneUtility.sms.schedule.app.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.black.phoneUtility.R;
import org.black.phoneUtility.sms.schedule.SmsScheduleConstant;
import org.black.phoneUtility.sms.schedule.app.fragment.SmsScheduleAddEditMessageFragment;
import org.black.phoneUtility.sms.schedule.content.broadcastReceiver.SentSmsBroadcastReceiver;
import org.black.phoneUtility.sms.schedule.provider.SmsScheduleContract;
import org.black.phoneUtility.sms.schedule.service.SmsScheduleService;
import org.black.phoneUtility.util.AlarmManagerUtility;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;

public class SmsScheduleSendingService extends IntentService {
	private static final String TAG = SmsScheduleSendingService.class.getName();
	private static final String LOG_TAG = SmsScheduleSendingService.class
			.getName();

	public static final String ARGUMENTS_SMS_SCHEDULE_ACTION_TYPE = "arguments_sms_schedule_action_type";
	public static final String ARGUMENTS_SMS_SCHEDULE_ID = "arguments_sms_schedule_id";
	public static final String ARGUMENTS_SMS_SCHEDULE_SHOULD_ACTIVATED = "arguments_sms_should_activated";
	public static final String ARGUMENTS_SMS_SCHEDULE_LAST_MODIFICATION_DATE_TIME = "arguments_schedule_last_modification_date_time";

	public static final int ARGUMENTS_SMS_SCHEDULE_ACTION_TYPE_SINGLE = 128;
	public static final int ARGUMENTS_SMS_SCHEDULE_ACTION_TYPE_RECHECK = 129;

	private static final long INVALID_ARGUMENTS_SMS_SCHEDULE_ID = -1l;
	private static final long INVALID_ARGUMENTS_SMS_SCHEDULE_LAST_MODIFICATION_DATE_TIME = -1l;

	private static final long one_second_in_milliseconds = 1000l;

	private SmsScheduleService scheduleService;
	private WakeLock wakeLock;

	private class WriteScheduleSendingRecordRecipientRunnable implements
			Runnable {
		private Context context;
		private String message;
		private String destinationAddress;
		private long sendingRecordId;

		public WriteScheduleSendingRecordRecipientRunnable(Context context,
				String message, String destinationAddress, long sendingRecordId) {
			this.context = context;
			this.message = message;
			this.destinationAddress = destinationAddress;
			this.sendingRecordId = sendingRecordId;
		}

		@Override
		public void run() {
			SmsManager smsManager = SmsManager.getDefault();
			if (this.message
					.indexOf(SmsScheduleAddEditMessageFragment.NAME_TAG) > -1) {
				Cursor contactCursor = null;
				try {
					Uri phoneLookupUri = Uri.withAppendedPath(
							PhoneLookup.CONTENT_FILTER_URI,
							Uri.encode(destinationAddress));
					contactCursor = context.getContentResolver().query(
							phoneLookupUri,
							new String[] { PhoneLookup.NUMBER,
									PhoneLookup.DISPLAY_NAME }, null, null,
							null);

					if (contactCursor != null) {
						for (contactCursor.moveToFirst(); contactCursor
								.isAfterLast() == false; contactCursor
								.moveToNext()) {
							String displayName = contactCursor
									.getString(contactCursor
											.getColumnIndexOrThrow(PhoneLookup.DISPLAY_NAME));
							if (PhoneNumberUtils
									.compare(
											context,
											destinationAddress,
											contactCursor.getString(contactCursor
													.getColumnIndexOrThrow(PhoneLookup.NUMBER))) == true) {
								if (TextUtils.isEmpty(displayName) == false) {
									this.message = this.message
											.replace(
													SmsScheduleAddEditMessageFragment.NAME_TAG,
													displayName);
									break;
								}
							}
						}
					}
				} finally {
					if (contactCursor != null) {
						try {
							contactCursor.close();
						} catch (Throwable throwable) {
							Log.e(LOG_TAG, "Fail to close Cursor", throwable);
						}
					}
				}
			}
			if (this.message
					.indexOf(SmsScheduleAddEditMessageFragment.NAME_TAG) > -1) {
				this.message = this.message.replace(
						SmsScheduleAddEditMessageFragment.NAME_TAG, "");
			}
			if (this.message
					.indexOf(SmsScheduleAddEditMessageFragment.FIRST_NAME_TAG) > -1) {
				Cursor contactCursor = null;
				try {
					Uri phoneLookupUri = Uri.withAppendedPath(
							PhoneLookup.CONTENT_FILTER_URI,
							Uri.encode(destinationAddress));
					contactCursor = context
							.getContentResolver()
							.query(phoneLookupUri,
									new String[] { PhoneLookup.NUMBER,
											PhoneLookup._ID }, null, null, null);

					if (contactCursor != null) {
						for (contactCursor.moveToFirst(); contactCursor
								.isAfterLast() == false; contactCursor
								.moveToNext()) {
							if (PhoneNumberUtils
									.compare(
											context,
											destinationAddress,
											contactCursor.getString(contactCursor
													.getColumnIndexOrThrow(PhoneLookup.NUMBER))) == true) {
								Cursor nameCursor = null;
								try {
									nameCursor = getContentResolver()
											.query(ContactsContract.Data.CONTENT_URI,
													new String[] { ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME },
													ContactsContract.Data.MIMETYPE
															+ " = ?"
															+ " AND "
															+ ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID
															+ " = ?",
													new String[] {
															ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE,
															String.valueOf(contactCursor
																	.getLong(contactCursor
																			.getColumnIndexOrThrow(PhoneLookup._ID))) },
													null);
									if (nameCursor != null
											&& nameCursor.moveToFirst() == true) {
										String givenName = nameCursor
												.getString(nameCursor
														.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
										if (TextUtils.isEmpty(givenName) == false) {
											this.message = this.message
													.replace(
															SmsScheduleAddEditMessageFragment.FIRST_NAME_TAG,
															givenName);
											break;
										}
									}
								} catch (Throwable throwable) {
									Log.e(LOG_TAG, "Fail to query given name!",
											throwable);
								} finally {
									if (nameCursor != null) {
										try {
											nameCursor.close();
										} catch (Throwable th) {
											Log.e(LOG_TAG,
													"Fail to close nameCursor",
													th);
										}
									}
								}
							}
						}
					}
				} finally {
					if (contactCursor != null) {
						try {
							contactCursor.close();
						} catch (Throwable throwable) {
							Log.e(LOG_TAG, "Fail to close Cursor", throwable);
						}
					}
				}
			}
			if (this.message
					.indexOf(SmsScheduleAddEditMessageFragment.FIRST_NAME_TAG) > -1) {
				this.message = this.message.replace(
						SmsScheduleAddEditMessageFragment.FIRST_NAME_TAG, "");
			}
			if (this.message
					.indexOf(SmsScheduleAddEditMessageFragment.NICKNAME_TAG) > -1) {
				Cursor contactCursor = null;
				try {
					Uri phoneLookupUri = Uri.withAppendedPath(
							PhoneLookup.CONTENT_FILTER_URI,
							Uri.encode(destinationAddress));
					contactCursor = context
							.getContentResolver()
							.query(phoneLookupUri,
									new String[] { PhoneLookup.NUMBER,
											PhoneLookup._ID }, null, null, null);

					if (contactCursor != null) {
						for (contactCursor.moveToFirst(); contactCursor
								.isAfterLast() == false; contactCursor
								.moveToNext()) {
							if (PhoneNumberUtils
									.compare(
											context,
											destinationAddress,
											contactCursor.getString(contactCursor
													.getColumnIndexOrThrow(PhoneLookup.NUMBER))) == true) {
								Cursor nickNameCursor = null;
								try {
									nickNameCursor = getContentResolver()
											.query(ContactsContract.Data.CONTENT_URI,
													new String[] { ContactsContract.CommonDataKinds.Nickname.NAME },
													ContactsContract.Data.MIMETYPE
															+ " = ?"
															+ " AND "
															+ ContactsContract.CommonDataKinds.Nickname.CONTACT_ID
															+ " = ?",
													new String[] {
															ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE,
															String.valueOf(contactCursor
																	.getLong(contactCursor
																			.getColumnIndexOrThrow(PhoneLookup._ID))) },
													null);
									if (nickNameCursor != null
											&& nickNameCursor.moveToFirst() == true) {
										String nickName = nickNameCursor
												.getString(nickNameCursor
														.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Nickname.NAME));
										if (TextUtils.isEmpty(nickName) == false) {
											this.message = this.message
													.replace(
															SmsScheduleAddEditMessageFragment.NICKNAME_TAG,
															nickName);
											break;
										}
									}
								} catch (Throwable throwable) {
									Log.e(LOG_TAG, "Fail to query nick name!",
											throwable);
								} finally {
									if (nickNameCursor != null) {
										try {
											nickNameCursor.close();
										} catch (Throwable th) {
											Log.e(LOG_TAG,
													"Fail to close nameCursor",
													th);
										}
									}
								}
							}
						}
					}
				} finally {
					if (contactCursor != null) {
						try {
							contactCursor.close();
						} catch (Throwable throwable) {
							Log.e(LOG_TAG, "Fail to close Cursor", throwable);
						}
					}
				}
			}
			if (this.message
					.indexOf(SmsScheduleAddEditMessageFragment.NICKNAME_TAG) > -1) {
				this.message = this.message.replace(
						SmsScheduleAddEditMessageFragment.NICKNAME_TAG, "");
			}
			ArrayList<String> messages = smsManager.divideMessage(message);

			ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
			ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();
			int legnth = messages.size();
			for (int i = 0; i < legnth; i++) {
				String dividedMessage = messages.get(i);
				ContentValues insertSendingRecordRecipientscContentValues = new ContentValues();
				insertSendingRecordRecipientscContentValues
						.put(SmsScheduleContract.SmsScheduleSendingRecordRecipients.REFERENCE_SCHEDULE_SENDING_RECORD_ID,
								sendingRecordId);
				insertSendingRecordRecipientscContentValues
						.put(SmsScheduleContract.SmsScheduleSendingRecordRecipients.MESSAGE,
								dividedMessage);
				insertSendingRecordRecipientscContentValues
						.put(SmsScheduleContract.SmsScheduleSendingRecordRecipients.PHONE_NUMBER,
								destinationAddress);
				insertSendingRecordRecipientscContentValues
						.put(SmsScheduleContract.SmsScheduleSendingRecordRecipients.CREATION_DATE_TIME,
								System.currentTimeMillis());
				Uri uri = context
						.getContentResolver()
						.insert(SmsScheduleContract.SmsScheduleSendingRecordRecipients.CONTENT_URI,
								insertSendingRecordRecipientscContentValues);
				Intent sentIntent = new Intent(
						SmsScheduleConstant.SMS_SENT_INTENT);
				sentIntent.setData(uri);
				sentIntent.putExtra(
						SentSmsBroadcastReceiver.ARGUMENTS_SENDING_RECORD_ID,
						Long.valueOf(sendingRecordId));
				PendingIntent sentPendingIntent = PendingIntent.getBroadcast(
						context, 0, sentIntent,
						PendingIntent.FLAG_UPDATE_CURRENT);

				Intent deliverIntent = new Intent(
						SmsScheduleConstant.SMS_DELIVERY_INTENT);
				deliverIntent.setData(uri);
				PendingIntent deliverPendingIntent = PendingIntent
						.getBroadcast(context, 0, deliverIntent,
								PendingIntent.FLAG_UPDATE_CURRENT);
				sentIntents.add(sentPendingIntent);
				deliveryIntents.add(deliverPendingIntent);

			}
			smsManager.sendMultipartTextMessage(destinationAddress, null,
					messages, sentIntents, deliveryIntents);
		}
	}

	public SmsScheduleSendingService() {
		super(SmsScheduleSendingService.class.getName());
		this.scheduleService = new SmsScheduleService(this);
	}

	@Override
	public void onCreate() {
		super.onCreate();
		PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
		wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
				SmsScheduleSendingService.TAG);
		wakeLock.acquire();
		Log.i(LOG_TAG, "Acquire WakeLock");
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		if (wakeLock != null && wakeLock.isHeld() == true) {
			wakeLock.release();
			wakeLock = null;
			Log.i(LOG_TAG, "Release WakeLock");
		}
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		Bundle arguments = intent.getExtras();
		if (arguments != null) {
			AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
			int actionType = arguments.getInt(
					ARGUMENTS_SMS_SCHEDULE_ACTION_TYPE,
					ARGUMENTS_SMS_SCHEDULE_ACTION_TYPE_SINGLE);
			Log.v(LOG_TAG, "actionType: " + actionType);
			if (actionType == ARGUMENTS_SMS_SCHEDULE_ACTION_TYPE_SINGLE) {
				boolean shouldActivated = arguments.getBoolean(
						ARGUMENTS_SMS_SCHEDULE_SHOULD_ACTIVATED, false);
				long smsScheduleId = arguments.getLong(
						ARGUMENTS_SMS_SCHEDULE_ID,
						INVALID_ARGUMENTS_SMS_SCHEDULE_ID);
				long lastModificationDateTime = arguments
						.getLong(
								ARGUMENTS_SMS_SCHEDULE_LAST_MODIFICATION_DATE_TIME,
								INVALID_ARGUMENTS_SMS_SCHEDULE_LAST_MODIFICATION_DATE_TIME);
				Uri dataUri = ContentUris.withAppendedId(
						SmsScheduleContract.SmsSchedules.CONTENT_URI,
						smsScheduleId);
				if (shouldActivated == false) {
					Intent cancelIntenty = new Intent(this,
							SmsScheduleSendingService.class);
					cancelIntenty.setData(dataUri);
					PendingIntent cancelPendingIntent = PendingIntent
							.getService(this, 0, cancelIntenty,
									PendingIntent.FLAG_CANCEL_CURRENT);
					alarmManager.cancel(cancelPendingIntent);
					cancelPendingIntent.cancel();
					Log.d(LOG_TAG, "Cancel SMS Schedule. dataUri:" + dataUri);
					return;
				}
				if (smsScheduleId != INVALID_ARGUMENTS_SMS_SCHEDULE_ID
						&& lastModificationDateTime != INVALID_ARGUMENTS_SMS_SCHEDULE_LAST_MODIFICATION_DATE_TIME) {
					Cursor scheduleCursor = null;
					try {
						scheduleCursor = getContentResolver().query(
								SmsScheduleContract.SmsSchedules.CONTENT_URI,
								null,
								SmsScheduleContract.SmsSchedules._ID + " = ? ",
								new String[] { String.valueOf(smsScheduleId) },
								null);
						if (scheduleCursor.moveToFirst() == true) {
							this.handlingSchedule(alarmManager, dataUri,
									scheduleCursor, lastModificationDateTime);
						} else {
							Log.d(LOG_TAG,
									"smsScheduleId does not exist. smsScheduleId: "
											+ smsScheduleId);
						}
					} catch (Throwable throwable) {
						Log.e(LOG_TAG, "Query SMS Schedule Error!", throwable);
					} finally {
						if (scheduleCursor != null) {
							try {
								scheduleCursor.close();
							} catch (Throwable th) {
								Log.e(LOG_TAG, "Fail to close scheduleCursor!",
										th);
							}
						}
					}
				} else {
					Log.e(LOG_TAG, "smsScheduleId: " + smsScheduleId
							+ ", lastModificationDateTime: "
							+ lastModificationDateTime);
				}
			} else if (actionType == ARGUMENTS_SMS_SCHEDULE_ACTION_TYPE_RECHECK) {
				Cursor scheduleCursor = null;
				try {
					scheduleCursor = getContentResolver().query(
							SmsScheduleContract.SmsSchedules.CONTENT_URI,
							null,
							SmsScheduleContract.SmsSchedules.IS_ACTIVATED
									+ " = ? ",
							new String[] { String.valueOf(1) }, null);
					if (scheduleCursor.moveToFirst() == true) {
						long smsScheduleId = scheduleCursor
								.getLong(scheduleCursor
										.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules._ID));
						long lastModificationDateTime = scheduleCursor
								.getLong(scheduleCursor
										.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.MODIFICATION_DATE_TIME));
						Uri dataUri = ContentUris.withAppendedId(
								SmsScheduleContract.SmsSchedules.CONTENT_URI,
								smsScheduleId);
						do {
							Intent cancelIntenty = new Intent(this,
									SmsScheduleSendingService.class);
							cancelIntenty.setData(dataUri);
							PendingIntent cancelPendingIntent = PendingIntent
									.getBroadcast(this, 0, cancelIntenty,
											PendingIntent.FLAG_CANCEL_CURRENT);
							alarmManager.cancel(cancelPendingIntent);
							cancelPendingIntent.cancel();
							this.handlingSchedule(alarmManager, dataUri,
									scheduleCursor, lastModificationDateTime);
						} while (scheduleCursor.moveToNext() == true);
					} else {
						Log.i(LOG_TAG, "No activated SMS Schedule exist!");
					}
				} catch (Throwable throwable) {
					Log.e(LOG_TAG, "Query SMS Schedule Error!", throwable);
				} finally {
					if (scheduleCursor != null) {
						try {
							scheduleCursor.close();
						} catch (Throwable th) {
							Log.e(LOG_TAG, "Fail to close scheduleCursor!", th);
						}
					}
				}
			} else {
				Log.e(LOG_TAG, "actionType: " + actionType
						+ " does not support!");
			}
		} else {
			Log.e(LOG_TAG, "No arguments in intent!");
		}

	}

	private void handlingSchedule(AlarmManager alarmManager, Uri dataUri,
			Cursor scheduleCursor, long lastModificationDateTime) {
		long smsScheduleId = scheduleCursor.getLong(scheduleCursor
				.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules._ID));
		boolean isActivated = (1 == scheduleCursor
				.getInt(scheduleCursor
						.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.IS_ACTIVATED))) ? true
				: false;
		if (isActivated == true) {
			long modificationDateTime = scheduleCursor
					.getLong(scheduleCursor
							.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.MODIFICATION_DATE_TIME));
			if (lastModificationDateTime == modificationDateTime) {
				int repeatType = scheduleCursor
						.getInt(scheduleCursor
								.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.REPEAT_TYPE));

				long currentTimeInMillis = System.currentTimeMillis();
				long nextScheduleDateTime = scheduleCursor
						.getLong(scheduleCursor
								.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.NEXT_SCHEDULE_DATE_TIME));

				if (currentTimeInMillis - nextScheduleDateTime > 0
						&& currentTimeInMillis - nextScheduleDateTime < one_second_in_milliseconds * 10l) {

					SharedPreferences sharedPreferences = PreferenceManager
							.getDefaultSharedPreferences(this);

					boolean doNotSendOnRoaming = sharedPreferences
							.getBoolean(
									getString(R.string.preference_sms_schedule_do_not_send_schedule_message_on_roaming),
									false);

					TelephonyManager telephonyService = (TelephonyManager) this
							.getSystemService(Context.TELEPHONY_SERVICE);
					if (doNotSendOnRoaming == false
							|| (doNotSendOnRoaming == true && telephonyService
									.isNetworkRoaming() == false)) {
						String message = scheduleCursor
								.getString(scheduleCursor
										.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.MESSAGE));
						if (TextUtils.isEmpty(message) == false) {
							ArrayList<String> phoneNumbers = new ArrayList<String>();
							List<Long> recipientIds = new ArrayList<Long>();
							Cursor recipientsCursor = null;
							try {
								recipientsCursor = getContentResolver()
										.query(SmsScheduleContract.SmsScheduleRecipients.CONTENT_URI,
												new String[] {
														SmsScheduleContract.SmsScheduleRecipients._ID,
														SmsScheduleContract.SmsScheduleRecipients.PHONE_NUMBER },
												SmsScheduleContract.SmsScheduleRecipients.REFERENCE_SMS_SCHEDULE_ID
														+ " = ? ",
												new String[] { String
														.valueOf(smsScheduleId) },
												null);
								if (recipientsCursor != null) {
									for (recipientsCursor.moveToFirst(); recipientsCursor
											.isAfterLast() == false; recipientsCursor
											.moveToNext()) {
										recipientIds
												.add(recipientsCursor
														.getLong(recipientsCursor
																.getColumnIndexOrThrow(SmsScheduleContract.SmsScheduleRecipients._ID)));
										phoneNumbers
												.add(recipientsCursor
														.getString(recipientsCursor
																.getColumnIndexOrThrow(SmsScheduleContract.SmsScheduleRecipients.PHONE_NUMBER)));
									}
								}
							} finally {
								if (recipientsCursor != null) {
									try {
										recipientsCursor.close();
									} catch (Throwable throwable) {
										Log.e(LOG_TAG,
												"Fail to close recipientsCursor",
												throwable);
									}
								}
							}

							if (phoneNumbers.size() > 0) {
								ContentValues sendingRecordContentValues = new ContentValues();
								sendingRecordContentValues
										.put(SmsScheduleContract.SmsScheduleSendingRecords.MESSAGE,
												message);
								sendingRecordContentValues
										.put(SmsScheduleContract.SmsScheduleSendingRecords.IS_VALID,
												1);
								sendingRecordContentValues
										.put(SmsScheduleContract.SmsScheduleSendingRecords.CREATION_DATE_TIME,
												currentTimeInMillis);
								sendingRecordContentValues
										.put(SmsScheduleContract.SmsScheduleSendingRecords.REFERENCE_SMS_SCHEDULE_ID,
												smsScheduleId);
								Uri sendingRecordUri = getContentResolver()
										.insert(SmsScheduleContract.SmsScheduleSendingRecords.CONTENT_URI,
												sendingRecordContentValues);

								Long sendingRecordId = Long
										.valueOf(sendingRecordUri
												.getLastPathSegment());
								int phoneNumbersLength = phoneNumbers.size();
								for (int i = 0; i < phoneNumbersLength; i++) {
									String phoneNumber = phoneNumbers.get(i);
									new Thread(
											new WriteScheduleSendingRecordRecipientRunnable(
													this, message, phoneNumber,
													sendingRecordId)).start();
								}
							}
						}
					}
				} else {
					Log.i(LOG_TAG,
							"currentTimeInMillis - nextScheduleDateTime > 1000l"
									+ currentTimeInMillis + ":"
									+ new Date(currentTimeInMillis) + ":"
									+ nextScheduleDateTime
									+ new Date(nextScheduleDateTime));
				}

				boolean shouldTurnOff = false;
				Long nextScheduleAfterNextDateTime = this.scheduleService
						.nextScheduleTimeInMilliseconds(scheduleCursor);
				Log.d(LOG_TAG,
						new Date(nextScheduleAfterNextDateTime).toString());
				switch (repeatType) {
				case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_NONE:
					if (nextScheduleAfterNextDateTime <= currentTimeInMillis) {
						Log.d(LOG_TAG,
								"repeatType : "
										+ repeatType
										+ ", nextScheduleAfterNextDateTime <= currentTimeInMillis");
						shouldTurnOff = true;
					}
					break;
				case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_EVERY_SECOND:
				case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_EVERY_MINUTE:
				case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_HOURLY:
				case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_DAILY:
				case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_MONTHLY:
				case SmsScheduleContract.SmsSchedules.REPEAT_TYPE_YEARLY:
					int endType = scheduleCursor
							.getInt(scheduleCursor
									.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_TYPE));
					if (SmsScheduleContract.SmsSchedules.END_TYPE_NEVER == endType) {
						Log.d(LOG_TAG, "repeatType : " + repeatType
								+ ", endType: " + endType);
						shouldTurnOff = false;
					} else if (SmsScheduleContract.SmsSchedules.END_TYPE_AFTER_TIMES == endType) {
						int endCount = scheduleCursor
								.getInt(scheduleCursor
										.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_AFTER_TIMES));
						if (this.scheduleService.getSendingCount(smsScheduleId) >= endCount) {
							Log.d(LOG_TAG, "repeatType : " + repeatType
									+ ", endType: " + endType + ", endCount: "
									+ endCount);
							shouldTurnOff = true;
						}
					} else if (SmsScheduleContract.SmsSchedules.END_TYPE_ON_SPECIFIC_DATETIME == endType) {
						long endOnSpecificDateTime = scheduleCursor
								.getLong(scheduleCursor
										.getColumnIndexOrThrow(SmsScheduleContract.SmsSchedules.END_ON_SPECIFIC_DATATIME));
						Calendar calendar = Calendar.getInstance();
						calendar.setTimeInMillis(endOnSpecificDateTime);
						calendar.set(Calendar.MILLISECOND, 0);
						calendar.set(Calendar.SECOND, 0);
						if (nextScheduleAfterNextDateTime >= endOnSpecificDateTime) {
							Log.d(LOG_TAG,
									"repeatType : "
											+ repeatType
											+ ", endType: "
											+ endType
											+ ", nextScheduleAfterNextDateTime: "
											+ nextScheduleAfterNextDateTime
											+ ", endOnSpecificDateTime: "
											+ calendar.getTimeInMillis());
							shouldTurnOff = true;
						}
					} else {
						Log.e(LOG_TAG, "endType: " + endType);
					}
					break;
				default:
					throw new IllegalArgumentException("Unknown repeatType: "
							+ repeatType);
				}
				ArrayList<ContentProviderOperation> contentProviderOperations = new ArrayList<ContentProviderOperation>();
				ContentValues updateSmsScheduleContentValues = new ContentValues();
				updateSmsScheduleContentValues
						.put(SmsScheduleContract.SmsSchedules.MODIFICATION_DATE_TIME,
								currentTimeInMillis);
				if (shouldTurnOff == true) {
					updateSmsScheduleContentValues.put(
							SmsScheduleContract.SmsSchedules.IS_ACTIVATED, 0);
					contentProviderOperations
							.add(ContentProviderOperation.newUpdate(dataUri)
									.withValues(updateSmsScheduleContentValues)
									.build());
					ContentValues updateSendingRecordContentValues = new ContentValues();
					updateSendingRecordContentValues
							.put(SmsScheduleContract.SmsScheduleSendingRecords.IS_VALID,
									0);
					contentProviderOperations
							.add(ContentProviderOperation
									.newUpdate(
											SmsScheduleContract.SmsScheduleSendingRecords.CONTENT_URI)
									.withValues(
											updateSendingRecordContentValues)
									.withSelection(
											SmsScheduleContract.SmsScheduleSendingRecords.REFERENCE_SMS_SCHEDULE_ID
													+ " = ? ",
											new String[] { String
													.valueOf(smsScheduleId) })
									.build());
				} else {
					updateSmsScheduleContentValues
							.put(SmsScheduleContract.SmsSchedules.NEXT_SCHEDULE_DATE_TIME,
									nextScheduleAfterNextDateTime);
					contentProviderOperations
							.add(ContentProviderOperation.newUpdate(dataUri)
									.withValues(updateSmsScheduleContentValues)
									.build());

					Intent scheduleSendingIntent = new Intent(this,
							SmsScheduleSendingService.class);
					scheduleSendingIntent.setData(dataUri);
					scheduleSendingIntent.putExtra(ARGUMENTS_SMS_SCHEDULE_ID,
							smsScheduleId);
					scheduleSendingIntent.putExtra(
							ARGUMENTS_SMS_SCHEDULE_SHOULD_ACTIVATED, true);
					scheduleSendingIntent.putExtra(
							ARGUMENTS_SMS_SCHEDULE_LAST_MODIFICATION_DATE_TIME,
							currentTimeInMillis);
					PendingIntent pendingIntent = PendingIntent.getService(
							this, 0, scheduleSendingIntent,
							PendingIntent.FLAG_CANCEL_CURRENT);
					AlarmManagerUtility.setAlarm(alarmManager,
							AlarmManager.RTC, nextScheduleAfterNextDateTime,
							pendingIntent);
				}
				try {
					ContentProviderResult[] applyBatchResult = this
							.getContentResolver().applyBatch(
									SmsScheduleContract.AUTHORITY,
									contentProviderOperations);
					Log.v(LOG_TAG, Arrays.toString(applyBatchResult));
				} catch (Throwable throwable) {
					Log.e(LOG_TAG, "switch off apply patch fail!", throwable);
				}
			} else {
				Log.i(LOG_TAG, "lastModificationDateTime: "
						+ lastModificationDateTime + ", modificationDateTime: "
						+ modificationDateTime);
			}
		} else {
			Log.i(LOG_TAG, "SMS Schedule is inactivated! smsScheduleId: "
					+ smsScheduleId);
		}

	}
}
